﻿using JESAI.HttpRequestMonitor.Core.Abstracts;
using JESAI.HttpRequestMonitor.Options;
using JESAI.HttpRequestMonitor.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace JESAI.HttpRequestMonitor.Middlewares
{
    internal class DefaultRequestMonitorMiddleware
    {
        private readonly RequestDelegate _next;

        public IRequestModelBuilder RequestModelBuilder { get; }

        public IHttpInvokeProcesser InvokeProcesser { get; }

        public HttpRequestMonitorOptions Options { get; }

        /// <summary>
        /// 日志记录器
        /// </summary>
        public ILogger<DefaultRequestMonitorMiddleware> Logger { get; }

        public DefaultRequestMonitorMiddleware(RequestDelegate next, IOptions<HttpRequestMonitorOptions> options, IRequestModelBuilder requestModelBuilder, IHttpInvokeProcesser invokeProcesser, ILogger<DefaultRequestMonitorMiddleware> logger)
        {
            _next = next;
            RequestModelBuilder = requestModelBuilder;
            InvokeProcesser = invokeProcesser;
            Options = options.Value;
            Logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                if (context.Request.Path.HasValue && context.Request.Path.Value == "/")
                {
                    await _next(context);
                    return;
                }

                if (context.IsGrpcRequest())
                {
                    await InvokeGrpcAsync(context);
                }
                else
                {
                    await InvokeHttpAsync(context);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "An error occurred while processing the request.");
                throw;
            }
        }

        /// <summary>
        /// 处理HTTP请求
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task InvokeHttpAsync(HttpContext context)
        {
            if (HttpRequestUtil.FilterRequest(context, Options))
            {
                await _next(context);
                return;
            }

            var stopwatch = Stopwatch.StartNew();
            stopwatch.Start();

            ConfigTrace(context);

            string requestBody = await HttpRequestUtil.GetRequestBodyAsync(context);

            var originalBodyStream = context.Response.Body;

            var responseMemoryStream = new MemoryStream();

            try
            {
                context.Response.Body = responseMemoryStream;

                await _next(context);
            }
            finally
            {
                stopwatch.Stop();

                string responseBody = await HttpRequestUtil.GetResponseBodyAsync(context);

                context.Items.Add(SysConst.HttpRequestMonitorRequestBody, requestBody);
                context.Items.Add(SysConst.HttpRequestMonitorResponseBody, responseBody);

                await responseMemoryStream.CopyToAsync(originalBodyStream);

                responseMemoryStream.Dispose();

                if (!string.IsNullOrEmpty(context.Request.Path))
                {
                    InvokeProcesser.Process(context, stopwatch);
                }
            }
        }

        private async Task InvokeGrpcAsync(HttpContext context)
        {
            var stopwatch = Stopwatch.StartNew();
            stopwatch.Start();

            ConfigTrace(context);

            try
            {

                await _next(context);
            }
            finally
            {
                stopwatch.Stop();

                string responseBody = string.Empty;
                string requestBody = string.Empty;

                if (context.Items.ContainsKey(SysConst.HttpRequestMonitorGrpcRequest))
                {
                    requestBody = JsonConvert.SerializeObject(context.Items[SysConst.HttpRequestMonitorGrpcRequest]);
                }

                if (context.Items.ContainsKey(SysConst.HttpRequestMonitorGrpcResponse))
                {
                    responseBody = JsonConvert.SerializeObject(context.Items[SysConst.HttpRequestMonitorGrpcResponse]);
                }

                context.Items.Add(SysConst.HttpRequestMonitorRequestBody, requestBody);
                context.Items.Add(SysConst.HttpRequestMonitorResponseBody, responseBody);

                if (!string.IsNullOrEmpty(context.Request.Path))
                {
                    InvokeProcesser.Process(context, stopwatch);
                }
            }
        }

        /// <summary>
        /// 配置跟踪
        /// </summary>
        /// <param name="context"></param>
        private void ConfigTrace(HttpContext context)
        {
            //如果当前没有活动的跟踪，则创建一个新的跟踪
            if (Activity.Current == null)
            {
                //创建新的跟踪
                var activity = new Activity(SysConst.ActiveTraceName);
                //开启跟踪
                activity.Start();
                //将跟踪ID添加到baggage中
                activity.AddBaggage(SysConst.ActiveTraceId, activity.Id);
                //将跟踪创建时间添加http上下文中
                context.Items.Add(SysConst.ActiveTraceCreateTime, DateTime.Now);
                //将跟踪ID添加到http响应头中
                context.Response.Headers.Add(SysConst.ActiveTraceId, activity.SpanId.ToString());
                return;
            }

            var parentId = Activity.Current.GetBaggageItem(SysConst.ActiveTraceId);
            if (string.IsNullOrWhiteSpace(parentId))
            {
                //如果baggage中没有跟踪ID，则创建一个新的跟踪
                Activity.Current = null;
                //创建新的跟踪
                Activity activity = new Activity(SysConst.ActiveTraceName);
                //开启跟踪
                activity.Start();
                //将跟踪ID添加到baggage中
                activity.AddBaggage(SysConst.ActiveTraceId, activity.Id);
                //将跟踪创建时间添加http上下文中
                context.Items.Add(SysConst.ActiveTraceCreateTime, DateTime.Now);
                //将跟踪ID添加到http响应头中
                context.Response.Headers.Add(SysConst.ActiveTraceId, activity.SpanId.ToString());
            }
            else
            {
                // 创建一个新的Activity实例，用于分布式链路追踪
                // 使用系统常量中的预定义名称标识追踪类型
                Activity activity = new Activity(SysConst.ActiveTraceName);

                // 设置当前Activity的父级ID，建立调用链的层级关系
                // 确保当前上下文的追踪信息能正确传递到子级
                activity.SetParentId(Activity.Current.Id);

                // 正式启动该Activity，开始记录时间戳和追踪数据
                // 注意：需在try-catch块中使用以确保Stop()始终执行
                activity.Start();

                // 在行李项(Baggage)中添加追踪ID，用于跨服务上下文传递
                // 适用于需要透传的元数据（如：全链路日志关联）
                activity.AddBaggage(SysConst.ActiveTraceId, activity.Id);

                // 在HTTP上下文字典中记录追踪创建时间
                // 用于后续中间件计算请求处理耗时
                context.Items.Add(SysConst.ActiveTraceCreateTime, DateTime.Now);

                // 将当前Span的ID写入响应头，使客户端能获取当前请求的追踪标识
                // 适用于前后端联调或客户端日志关联场景
                context.Response.Headers.Add(
                    SysConst.ActiveTraceId,
                    activity.SpanId.ToString()
                );

            }
        }
    }
}
